// Copyright 2007 Google Inc. All Rights Reserved.

package net.sourceforge.perforce.core.internal;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import net.sourceforge.perforce.core.PerforceCorePlugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Provides support for finding and loading settings from files as specified in
 * the "P4CONFIG" envinronmental variable.
 *
 * @author steve.mckay@gmail.com (Steve McKay)
 */
@SuppressWarnings("nls")
public class P4ConfigLoader {

  /**
   * Map of P4CONFIG names to the property names used internally.
   */
  static Map<String, String> configPropertyNameMap = Maps.newHashMap();

  static {
    configPropertyNameMap.put("P4PORT", "port");
    configPropertyNameMap.put("P4CLIENT", "client");
    configPropertyNameMap.put("P4HOST", "host");
    configPropertyNameMap.put("P4USER", "user");
    configPropertyNameMap.put("P4CHARSET", "charset");
    configPropertyNameMap.put("P4PASSWD", "password");
    // all other values from P4CONFIG are unsupported
  }

  private final File path;
  private final String configFileName;

  public P4ConfigLoader(File path, String name) {
    this.path = path;
    this.configFileName = name;
  }

  public void applySettings(Map<String, Object> properties) {
    List<File> files = findFiles(configFileName, path);

    // walk backwards through the file list, applying properties
    // to our default properties.
    for (int i = files.size() - 1; i >= 0; i--) {
      applyConfigValues(files.get(i), properties);
    }
  }

  /**
   * Applies the values from the named p4 config file to the properties file.
   */
  private static void applyConfigValues(File file, Map<String, Object> props) {
    // create and load default properties
    Map<String, String> p4config = Maps.newHashMap();
    FileInputStream in = null;
    try {
      Properties p = new Properties();
      in = new FileInputStream(file);
      p.load(in);
      p4config = Maps.fromProperties(p);
    } catch (IOException e) {
      PerforceCorePlugin.logError("Couldn't load p4config file", e);
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
          // doh, double whammy!!!
          PerforceCorePlugin.logError("Couldn't close input stream.", e);
        }
      }
    }

    // copy recognized settings
    for (Map.Entry<String, String> entry : configPropertyNameMap.entrySet()) {
      String value = p4config.get(entry.getKey());
      if (value != null) {
        props.put(entry.getValue(), value);
      }
    }
  }

  /**
   * Returns a list of files matching the supplied name. The list will be
   * ordered by descending proximity to the root of the file system.
   */
  private static List<File> findFiles(final String name, File path) {

    int maxDirs = 20; // safety, max dirs to climb

    List<File> found = Lists.newArrayList();
    if (path != null) {
      FilenameFilter nameFilter = new FilenameFilter() {
        public boolean accept(File dir, String file) {
          return file != null && file.equals(name);
        }
      };
      while (path != null) {
        File[] matches = path.listFiles(nameFilter);
        if (matches != null && matches.length > 0) {
          found.add(matches[0]); // our tests is equals, so there can be only 1
        }
        // safety out
        if (--maxDirs == 0) {
          break;
        }
        path = path.getParentFile();
      }
    }
    return found;
  }
}
